#include "stm32f1xx_hal.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"

/* This macro is used to enable system support for timer delay. */
#define SYS_SUPPORT_OS          1
 
static uint16_t g_psc = 0;

#if (SYS_SUPPORT_OS == 1)

/* FreeRTOS is used here; users employing other operating systems can simply include other header files. */
#include "freertos.h"

static uint16_t g_fac_ms = 0;

/**
 * @brief When delaying at the microsecond level, lock the task scheduler to prevent SysTick 
 *        from affecting the precision of the delay.
 *
 * @note  Implemented based on FreeRTOS by default, users can modify the implementation to 
 *        support other operating systems.
 */
void delay_osschedlock(void)
{
	if(!xPortIsInsideInterrupt())
	{
		vTaskSuspendAll();
	}
}

/**
 * @brief  When delaying at the microsecond level, unlock the task scheduler.
 *
 * @note  Implemented based on FreeRTOS by default, users can modify the implementation to 
 *        support other operating systems.
 */
void delay_osschedunlock(void)
{
	if(!xPortIsInsideInterrupt())
	{
		xTaskResumeAll();
	}
}

#endif

/**
 * @brief Initialize the delay module.
 *
 * @param sysclk : The input clock frequency of the timer.
 */  
void delay_init(uint16_t sysclk)
{
    g_psc = sysclk - 1;                                 
}

/**
 * @brief Delay n microseconds
 *        Regardless of whether an OS is used, the method of clock extraction is employed for microsecond-level delays.
 * 
 * @param nus: n microseconds
 * 
 */
void delay_us(uint16_t nus)
{
	uint16_t arr = nus - 1;
    
#if SYS_SUPPORT_OS                          
    delay_osschedlock();                    
#endif
	
	__HAL_RCC_TIM2_CLK_ENABLE();
	TIM_HandleTypeDef TimHandle;
	TimHandle.Instance = TIM2;
	TimHandle.Init.Prescaler = g_psc;
	TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
	TimHandle.Init.Period = nus - 1;
	TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
	
	// Disable auto-reload because it is only used for timing once.
	TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
	
	HAL_TIM_Base_Init(&TimHandle);
	
	if( nus > 1)
	{
		__HAL_TIM_CLEAR_FLAG(&TimHandle, TIM_FLAG_UPDATE);
	}
	HAL_TIM_Base_Start(&TimHandle);
	
	// Wait for the delay time to elapse.
	while( __HAL_TIM_GET_FLAG(&TimHandle, TIM_FLAG_UPDATE) == RESET )
	{
		
	}
	
	// Stop timer
	HAL_TIM_Base_Stop(&TimHandle);
	__HAL_TIM_CLEAR_FLAG(&TimHandle, TIM_FLAG_UPDATE);
	HAL_TIM_Base_DeInit(&TimHandle);
	__HAL_RCC_TIM2_CLK_DISABLE();
	
#if SYS_SUPPORT_OS         
    delay_osschedunlock(); 
#endif 

}

/**
 * @brief Delay n milliseconds.
 */
void delay_ms(uint16_t nms)
{
	uint32_t restTime = nms * 1000;
	while(restTime > (uint32_t)65535)
	{
		delay_us((uint16_t)65535);
		restTime -= 65535;
	}
	if(restTime > 0)
	{
		delay_us((uint16_t)restTime);
	}
}

void HAL_Delay(uint32_t Delay)
{
     delay_ms((uint16_t)Delay);
}
